#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pd_linux.h"
#include "xconfig.h"
#include "xusb_common.h"
#include "xlibusb.h"
#ifndef _WIN32
    #include "libusb.h"
#endif

#ifdef __ANDROID__
    #include <jni.h>
    extern JavaVM *g_jvm;
#endif

// magic value
const unsigned int MAGIC_DEVICE_HANDLE = 0xAABBDDCC;
typedef struct _XUSBDeviceHandle
{
    unsigned int magic;
#ifdef __ANDROID__
    unsigned char bUsbHost; //0 libusb;1 android usb host
    JNIEnv *env;            //java environment
    jobject obj;            //usb host object
    jclass Cls;             //usb host class
    jmethodID control;      //control transfer
    jmethodID read;         //bulk read
    jmethodID write;        //bulk write
    jbyteArray tempBufferArr;
    unsigned char *pRecvBuffer;         // receive buffer
#endif
#ifndef _WIN32
    struct libusb_device_handle *dev_handle;   //libusb handle
    struct libusb_context *ctx;                //libusb context
#else
    struct usb_dev_handle *dev_handle;
#endif
    uint8_t inEndpoint;                 //in endpoint
    uint8_t outEndpoint;                //out endpoint
    unsigned int vid;                   //vendor id
    unsigned int pid;                   //product id
    char szSerialNumber[64];            // serial number
    int interfaceIndex;                 // interface index
    USBDEV_LOCK lock;
    char szManufacturer[64];
    char szModel[64];
    int rev;
    int currentSioSetData;
    int bcdDevice;
    int iSerialNumber;
} TXUSBDeviceHandle, *PXUSBDeviceHandle;


#ifdef __ANDROID__

#define MAX_ANDROID_PACKET_SIZE         16*1024
#define FIND_CLASS(var, className) \
    var = env->FindClass(className); \
    var = jclass(env->NewGlobalRef(var));

#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    var = env->GetStaticMethodID(clazz, methodName, methodDescriptor);

#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    var = env->GetMethodID(clazz, methodName, methodDescriptor);

#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
    var = env->GetFieldID(clazz, fieldName, fieldDescriptor);

#define GET_STATIC_FIELD_ID(var, clazz, varName, sig) \
    var = env->GetStaticFieldID(clazz, varName, sig);
#endif


XUDC_API_BEGIN

int IMPL_TransferRead(void *handle, unsigned char *data, int length, int *transferred,
                      unsigned int timeout);
int IMPL_TransferWrite(void *handle, unsigned char *data, int length, int *transferred,
                       unsigned int timeout);
int IMPL_ControlTransfer(void *handle, unsigned char bmRequestType, unsigned char bRequest, unsigned short wValue, unsigned short wIndex,
                         unsigned char *data, unsigned short wLength, unsigned int timeout);
#ifndef _WIN32
    void *xlibusbopen(libusb_context *context, libusb_device *dev, unsigned int vid, unsigned int pid, char *serial, int fd = 0);
#endif

#ifdef __ANDROID__
void *xusbhostopen(JavaVM *jvm, JNIEnv *env, jobject obj);

JNIEnv *GetJNIEnv(int *needsDetach)
{
    JNIEnv *env = NULL;
    jint result = -1;
    if(NULL == g_jvm)
    {
        return NULL;
    }
    if(g_jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
    {
        int status = g_jvm->AttachCurrentThread(&env, 0);
        if(status < 0)
        {
            return NULL;
        }
        *needsDetach = 1;
    }
    return env;
}

int HOSTAPI_Control(PXUSBDeviceHandle pXDev, int requestType, unsigned char request, int value, int index,  unsigned char *buffer, int length, int timeout)
{
    int ret = 0;
    int needsDetach = 0;
    JNIEnv *env = GetJNIEnv(&needsDetach);
    if(NULL == env)
    {
        return -1;
    }
    if(NULL != buffer && length > 0)
    {
        env->SetByteArrayRegion(pXDev->tempBufferArr, 0, length, (const jbyte *)buffer);
    }

    ret = env->CallIntMethod(pXDev->obj, pXDev->control, requestType, request, value, index, pXDev->tempBufferArr, length, timeout);
    if(ret > 0)
    {
        jbyte *jby = env->GetByteArrayElements(pXDev->tempBufferArr, NULL);
        if(NULL != jby && NULL != buffer)
        {
            memcpy(buffer, jby, ret);
            env->ReleaseByteArrayElements(pXDev->tempBufferArr, jby, 0);
        }
    }
    if(1 == needsDetach)
    {
        g_jvm->DetachCurrentThread();
    }
    return ret;
}

int HOSTAPI_Write(PXUSBDeviceHandle pXDev, unsigned char *buffer, int length, int timeout)
{
    int ret = 0;
    int needsDetach = 0;
    JNIEnv *env = GetJNIEnv(&needsDetach);
    if(NULL == env)
    {
        return -1;
    }
    env->SetByteArrayRegion(pXDev->tempBufferArr, 0, length, (const jbyte *)buffer);
    ret = env->CallIntMethod(pXDev->obj, pXDev->write, pXDev->tempBufferArr, length, timeout);
    if(1 == needsDetach)
    {
        g_jvm->DetachCurrentThread();
    }
    return ret;
}

int HOSTAPI_Read(PXUSBDeviceHandle pXDev, unsigned char *buffer, int length, int timeout)
{
    int ret = 0;
    int needsDetach = 0;
    JNIEnv *env = GetJNIEnv(&needsDetach);
    if(NULL == env)
    {
        return -1;
    }
    ret = env->CallIntMethod(pXDev->obj, pXDev->read, pXDev->tempBufferArr, length, timeout);
    if(ret > 0)
    {
        jbyte *jby = env->GetByteArrayElements(pXDev->tempBufferArr, NULL);
        memcpy(buffer, jby, ret > length ? length : ret);
        env->ReleaseByteArrayElements(pXDev->tempBufferArr, jby, 0);
    }
    if(1 == needsDetach)
    {
        g_jvm->DetachCurrentThread();
    }
    return ret;
}
#endif


int IMPL_ControlTransfer(void *handle, unsigned char bmRequestType, unsigned char bRequest, unsigned short wValue, unsigned short wIndex,
                         unsigned char *data, unsigned short wLength, unsigned int timeout)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
#ifdef __ANDROID__
    if(1 == pXUSBDeviceHandle->bUsbHost)
    {
        ret = HOSTAPI_Control(pXUSBDeviceHandle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
    }
    else
#endif
    {
#ifndef _WIN32
        ret = libusb_control_transfer(pXUSBDeviceHandle->dev_handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
#endif
    }
    return ret;
}


int IMPL_TransferWrite(void *handle, unsigned char *data, int length, int *transferred,
                       unsigned int timeout)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
#ifdef __ANDROID__
    if(1 == pXUSBDeviceHandle->bUsbHost)
    {
        if(length <= MAX_ANDROID_PACKET_SIZE)
        {
            *transferred = HOSTAPI_Write(pXUSBDeviceHandle, data, length, timeout);
        }
        else
        {
            int nOffset = 0;
            int nPacketLen = 0;
            while(nOffset < length)
            {
                nPacketLen = length - nOffset;
                if(nPacketLen > MAX_ANDROID_PACKET_SIZE)
                {
                    nPacketLen = MAX_ANDROID_PACKET_SIZE;
                }
                *transferred = HOSTAPI_Write(pXUSBDeviceHandle, data + nOffset, nPacketLen, timeout);
                if(*transferred < 0)
                {
                    break;
                }
                nOffset += *transferred;
            }
            *transferred = nOffset;
        }
        if(*transferred > 0)
        {
            ret = 0;
        }
        else if(*transferred = 0)
        {
            ret = -116;
        }
        else
        {
            ret = 0;
        }
    }
    else
#endif
    {
#ifndef _WIN32
        ret = libusb_bulk_transfer(pXUSBDeviceHandle->dev_handle, pXUSBDeviceHandle->outEndpoint, data, length, transferred, timeout);
#endif
    }
    return ret;
}


int IMPL_TransferRead(void *handle, unsigned char *data, int length, int *transferred,
                      unsigned int timeout)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
#ifdef __ANDROID__
    if(1 == pXUSBDeviceHandle->bUsbHost)
    {
        *transferred = HOSTAPI_Read(pXUSBDeviceHandle, data, length, timeout);
        if(*transferred > 0)
        {
            ret = 0;
        }
        else if(*transferred = 0)
        {
            ret = -116;
        }
        else
        {
            ret = 0;
        }
    }
    else
#endif
    {
        *transferred = 0;
#ifndef _WIN32
        ret = libusb_bulk_transfer(pXUSBDeviceHandle->dev_handle, pXUSBDeviceHandle->inEndpoint, data, length, transferred, timeout);
#endif
    }
    return ret;
}

#ifndef _WIN32
int mylibusb_get_string_descriptor(void *handle,
                                   uint8_t desc_index, uint16_t langid, unsigned char *data, int length)
{
    return IMPL_ControlTransfer(handle, LIBUSB_ENDPOINT_IN,
                                LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index),
                                langid, data, (uint16_t) length, 1000);
}

int mylibusb_get_string_descriptor_ascii(void *handle,
                                         uint8_t desc_index, unsigned char *data, int length)
{
    unsigned char tbuf[255]; /* Some devices choke on size > 255 */
    int r, si, di;
    uint16_t langid;

    /* Asking for the zero'th index is special - it returns a string
     * descriptor that contains all the language IDs supported by the
     * device. Typically there aren't many - often only one. Language
     * IDs are 16 bit numbers, and they start at the third unsigned char in the
     * descriptor. There's also no point in trying to read descriptor 0
     * with this function. See USB 2.0 specification section 9.6.7 for
     * more information.
     */

    if(desc_index == 0)
    {
        return LIBUSB_ERROR_INVALID_PARAM;
    }

    r = mylibusb_get_string_descriptor(handle, 0, 0, tbuf, sizeof(tbuf));
    if(r < 0)
    {
        return r;
    }

    if(r < 4)
    {
        return LIBUSB_ERROR_IO;
    }

    langid = tbuf[2] | (tbuf[3] << 8);

    r = mylibusb_get_string_descriptor(handle, desc_index, langid, tbuf,
                                       sizeof(tbuf));
    if(r < 0)
    {
        return r;
    }

    if(tbuf[1] != LIBUSB_DT_STRING)
    {
        return LIBUSB_ERROR_IO;
    }

    if(tbuf[0] > r)
    {
        return LIBUSB_ERROR_IO;
    }

    for(di = 0, si = 2; si < tbuf[0]; si += 2)
    {
        if(di >= (length - 1))
        {
            break;
        }

        if(tbuf[si + 1])  /* high unsigned char */
        {
            data[di++] = '?';
        }
        else
        {
            data[di++] = tbuf[si];
        }
    }

    data[di] = 0;
    return di;
}

void *xlibusbopen(libusb_context *context, libusb_device *dev, unsigned int vid, unsigned int pid, char *serial, int fd/* = 0*/)
{
    struct libusb_device_descriptor desc;
    int inEndpoint = 0;
    int outEndpoint = 0;
    int interfaceSelect = -1;
    int r = 0;
    r = libusb_get_device_descriptor(dev, &desc);
    if(r < 0)
    {
        return NULL;
    }
    for(int j = 0; j < desc.bNumConfigurations && (-1 == interfaceSelect); ++j)
    {
        struct libusb_config_descriptor *conf_desc = NULL;
        const struct libusb_endpoint_descriptor *endpoint = NULL;
        int ret = libusb_get_config_descriptor(dev, j, &conf_desc);
        if(ret >= 0)
        {
            for(int i = 0; i < conf_desc->bNumInterfaces && (-1 == interfaceSelect); i++)
            {
                for(j = 0; j < conf_desc->interface[i].num_altsetting; j++)
                {
                    for(int k = 0; k < conf_desc->interface[i].altsetting[j].bNumEndpoints; k++)
                    {
                        struct libusb_ss_endpoint_companion_descriptor *ep_comp = NULL;
                        endpoint = &conf_desc->interface[i].altsetting[j].endpoint[k];
                        if((endpoint->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK) && (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN))
                        {
                            if(!inEndpoint)
                            {
                                inEndpoint = endpoint->bEndpointAddress;
                            }
                        }
                        else if(endpoint->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK)
                        {
                            if(!outEndpoint)
                            {
                                outEndpoint = endpoint->bEndpointAddress;
                            }
                        }
                        if(inEndpoint)
                        {
                            interfaceSelect = conf_desc->interface[i].altsetting[j].bInterfaceNumber;
                            //break;
                        }
                    }
                }
            }
            libusb_free_config_descriptor(conf_desc);
        }
        if(!inEndpoint && !outEndpoint)
        {
            return NULL;
        }
#ifdef ZKDEBUG
        printf("inEndpoint=%d, outEndpoint=%d\r\n", inEndpoint, outEndpoint);
#endif
        libusb_device_handle *device_handle = NULL;
#ifdef __ANDROID__
        if(0 != fd)
        {
            libusb_open_fd(dev, &device_handle, fd);
        }
        else
#endif
        {
            libusb_open(dev, &device_handle);
        }
        if(NULL == device_handle)
        {
            return NULL;
        }

        if(libusb_kernel_driver_active(device_handle, interfaceSelect) == 1) //find out if kernel driver is attached
        {
            //printf("kernel active!!\r\n");
            if(libusb_detach_kernel_driver(device_handle, interfaceSelect) == 0) //detach it
            {
                //printf("kernel detech!!\r\n");
            }
            else
            {
                //printf("kernel no detech!!\r\n");
            }
        }
        else
        {
            //printf("kernel no active!!\r\n");
        }
        r = libusb_claim_interface(device_handle, interfaceSelect); //claim interface 0 (the first) of device (mine had jsut 1)
        if(r < 0)
        {
            libusb_reset_device(device_handle);
            r = libusb_claim_interface(device_handle, interfaceSelect);
        }
        if(r < 0)
        {
            libusb_close(device_handle);
            return NULL;
        }

        PXUSBDeviceHandle pDeviceHandle = new TXUSBDeviceHandle;
        memset(pDeviceHandle, 0x0, sizeof(TXUSBDeviceHandle));
#ifdef __ANDROID__
        pDeviceHandle->bUsbHost = 0;
#endif
        pDeviceHandle->magic = MAGIC_DEVICE_HANDLE;
        pDeviceHandle->ctx = context;
        pDeviceHandle->dev_handle = device_handle;
        pDeviceHandle->inEndpoint = inEndpoint;
        pDeviceHandle->outEndpoint = outEndpoint;
        pDeviceHandle->interfaceIndex = interfaceSelect;
        pDeviceHandle->vid = vid;
        pDeviceHandle->pid = pid;
        INITLOCK(pDeviceHandle->lock);
        if(NULL != serial)
        {
            strcpy(pDeviceHandle->szSerialNumber, serial);
        }
        else
        {
            mylibusb_get_string_descriptor_ascii(pDeviceHandle, desc.iSerialNumber, (unsigned char *)pDeviceHandle->szSerialNumber, 64);
        }
        mylibusb_get_string_descriptor_ascii(pDeviceHandle, desc.iProduct, (unsigned char *)pDeviceHandle->szModel, 64);
        mylibusb_get_string_descriptor_ascii(pDeviceHandle, desc.iManufacturer, (unsigned char *)pDeviceHandle->szManufacturer, 64);
        pDeviceHandle->rev = desc.bcdDevice;
        pDeviceHandle->bcdDevice = desc.bcdDevice;
        pDeviceHandle->iSerialNumber = desc.iSerialNumber;
        return (void *)pDeviceHandle;
    }
    return NULL;
}

#ifdef __ANDROID__
void *xusbhostopen(JavaVM *jvm, JNIEnv *env, jobject obj)
{
    g_jvm = jvm;
    PXUSBDeviceHandle pXUSBDeviceHandle = new TXUSBDeviceHandle;
    memset(pXUSBDeviceHandle, 0x0, sizeof(TXUSBDeviceHandle));
    pXUSBDeviceHandle->magic = MAGIC_DEVICE_HANDLE;
    pXUSBDeviceHandle->bUsbHost = 1;
    pXUSBDeviceHandle->env = env;
    do
    {
        pXUSBDeviceHandle->Cls = env->GetObjectClass(obj);
        pXUSBDeviceHandle->obj = (jobject)env->NewGlobalRef(obj);
        if(NULL == pXUSBDeviceHandle->Cls || NULL == pXUSBDeviceHandle->obj)
        {
            break;
        }
        GET_METHOD_ID(pXUSBDeviceHandle->control, pXUSBDeviceHandle->Cls, "control", "(IIII[BII)I");
        GET_METHOD_ID(pXUSBDeviceHandle->write, pXUSBDeviceHandle->Cls, "write", "([BII)I");
        GET_METHOD_ID(pXUSBDeviceHandle->read, pXUSBDeviceHandle->Cls, "read", "([BII)I");
        if(NULL == pXUSBDeviceHandle->control || NULL == pXUSBDeviceHandle->write || NULL == pXUSBDeviceHandle->read)
        {
            break;
        }
        jmethodID midGetVendorID = NULL;
        jmethodID midGetProductID = NULL;
        GET_METHOD_ID(midGetVendorID, pXUSBDeviceHandle->Cls, "getVendorID", "()I");
        GET_METHOD_ID(midGetProductID, pXUSBDeviceHandle->Cls, "getProductID", "()I");
        if(NULL != midGetVendorID)
        {
            pXUSBDeviceHandle->vid = env->CallIntMethod(pXUSBDeviceHandle->obj, midGetVendorID);
        }
        if(NULL != midGetProductID)
        {
            pXUSBDeviceHandle->pid = env->CallIntMethod(pXUSBDeviceHandle->obj, midGetProductID);
        }
        jmethodID midBCDDevice = NULL;
        jmethodID midISerialNumber = NULL;
        GET_METHOD_ID(midBCDDevice, pXUSBDeviceHandle->Cls, "getBCDDevice", "()I");
        GET_METHOD_ID(midISerialNumber, pXUSBDeviceHandle->Cls, "getISerialNum", "()I");
        if(NULL != midBCDDevice)
        {
            pXUSBDeviceHandle->bcdDevice = env->CallIntMethod(pXUSBDeviceHandle->obj, midBCDDevice);
        }
        if(NULL != midISerialNumber)
        {
            pXUSBDeviceHandle->iSerialNumber = env->CallIntMethod(pXUSBDeviceHandle->obj, midISerialNumber);
        }
        jbyteArray jbarr = env->NewByteArray(MAX_ANDROID_PACKET_SIZE);
        pXUSBDeviceHandle->tempBufferArr = (jbyteArray)env->NewGlobalRef(jbarr);
        env->DeleteLocalRef(jbarr);
        pXUSBDeviceHandle->pRecvBuffer = new unsigned char[MAX_ANDROID_PACKET_SIZE];
    } while(0);

    if(env->ExceptionOccurred() || env->ExceptionCheck())
    {
        env->ExceptionClear();
        if(NULL != pXUSBDeviceHandle->obj)
        {
            env->DeleteGlobalRef(pXUSBDeviceHandle->obj);
        }
        delete pXUSBDeviceHandle;
        return NULL;
    }

    if(NULL == pXUSBDeviceHandle->pRecvBuffer)
    {
        if(NULL != pXUSBDeviceHandle->obj)
        {
            env->DeleteGlobalRef(pXUSBDeviceHandle->obj);
        }
        delete pXUSBDeviceHandle;
        return NULL;
    }
    INITLOCK(pXUSBDeviceHandle->lock);
    return (void *)pXUSBDeviceHandle;
}
#endif
#endif


int APICALL x_libusb_enumdevice(int vid, int pid, TXUSBDevice deviceList[], int nMaxCount)
{
#ifndef _WIN32
    libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
    libusb_context *ctx = NULL; //a libusb session
    int r; //for return values
    ssize_t cnt; //holding number of devices in list
    int index = 0;
    r = libusb_init(&ctx); //initialize the library for the session we just declared
    if(r < 0)
    {
        printf("libusb_init failed, ret= %d\r\n", r);
        return 0;
    }

    cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
    if(cnt < 0)
    {
        printf("libusb_get_device_list failed, ret= %d\r\n", (int)cnt);
        libusb_exit(ctx);
        return 0;
    }

    for(int i = 0; i < cnt; i++)
    {
        struct libusb_device_descriptor desc;
        int r2 = libusb_get_device_descriptor(devs[i], &desc);
        if(r2 < 0)
        {
            printf("libusb_get_device_descriptor failed, ret= %d\r\n", r2);
            continue;
        }
#ifdef ZKDEBUG
        printf("vid=%d, desc.vid=%d, pid=%d, desc.pid=%d\r\n", vid, desc.idVendor, pid, desc.idProduct);
#endif
        if((0 == vid) ||
                (vid == desc.idVendor && (0 == pid || pid == desc.idProduct)))
        {
            memset(deviceList[index].szSerialNumber, 0x0, 64);
            deviceList[index].vid = desc.idVendor;
            deviceList[index].pid = desc.idProduct;
            deviceList[index].bus_number = libusb_get_bus_number(devs[i]);
            deviceList[index].device_address = libusb_get_device_address(devs[i]);
            struct libusb_device_handle *usb_p = NULL;
            r = libusb_open(devs[i], &usb_p);
            if(NULL != usb_p)
            {
                unsigned int nTickstart = GetTickCount();
                while(GetTickCount() - nTickstart < 2 * 1000)
                {
                    Sleep(10);
                    if(libusb_get_string_descriptor_ascii(usb_p, desc.iSerialNumber, (unsigned char *)deviceList[index].szSerialNumber, 64) >= 0)
                    {
                        break;
                    }
                    if(desc.idVendor != 0x1b55)
                    {
                        break;
                    }
                }
                libusb_close(usb_p);
            }
            index++;
        }

        if(index >= nMaxCount)
        {
            break;
        }
    }
    libusb_free_device_list(devs, 1); //free the list, unref the devices in it
    libusb_exit(ctx);
    return index;
#endif
}

int APICALL x_libusb_searchdevice(unsigned short vid, unsigned short pid, char *szSerialNumber, PXUSBDevice deviceInfo)
{
#ifndef _WIN32
    libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
    libusb_context *ctx = NULL; //a libusb session
    int r; //for return values
    int retVal = 0;
    ssize_t cnt; //holding number of devices in list
    r = libusb_init(&ctx); //initialize the library for the session we just declared
    if(r < 0)
    {
        return 0;
    }

    cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
    if(cnt < 0)
    {
        libusb_exit(ctx);
        return 0;
    }

    for(int i = 0; i < cnt; i++)
    {
        struct libusb_device_descriptor desc;
        int r2 = libusb_get_device_descriptor(devs[i], &desc);
        if(r2 < 0)
        {
            continue;
        }
        if(vid == desc.idVendor && pid == desc.idProduct)
        {
            struct libusb_device_handle *usb_p = NULL;
            libusb_open(devs[i], &usb_p);
            if(NULL != usb_p)
            {

                unsigned int nTickstart = GetTickCount();
                while(GetTickCount() - nTickstart < 2 * 1000)
                {
                    if(libusb_get_string_descriptor_ascii(usb_p, desc.iSerialNumber, (unsigned char *)deviceInfo->szSerialNumber, 64) >= 0)
                    {
                        break;
                    }
                    if(desc.idVendor != 0x1b55)
                    {
                        break;
                    }
                    Sleep(10);
                }
                libusb_close(usb_p);
            }
            if(strlen(szSerialNumber) <= 0 || strcmp(deviceInfo->szSerialNumber, szSerialNumber) == 0)
            {
                deviceInfo->vid = desc.idVendor;
                deviceInfo->pid = desc.idProduct;
                deviceInfo->bus_number = libusb_get_bus_number(devs[i]);
                deviceInfo->device_address = libusb_get_device_address(devs[i]);
                retVal = 1;
                break;
            }
        }
    }
    libusb_free_device_list(devs, 1); //free the list, unref the devices in it
    libusb_exit(ctx);
    return retVal;
#endif
}

void *APICALL x_libusb_opendevice(PXUSBDevice usbDevice)
{
#ifndef _WIN32
    libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
    libusb_device *dev = NULL;
    libusb_context *ctx = NULL; //a libusb session
    int r; //for return values
    ssize_t cnt; //holding number of devices in list
    r = libusb_init(&ctx); //initialize the library for the session we just declared
    if(r < 0)
    {
        return NULL;
    }

    cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
    if(cnt < 0)
    {
        libusb_exit(ctx);
        return NULL;
    }

    for(int i = 0; i < cnt; i++)
    {
        struct libusb_device_descriptor desc;
        int r2 = libusb_get_device_descriptor(devs[i], &desc);
        if(r2 < 0)
        {
            continue;
        }
        if(usbDevice->vid == desc.idVendor && usbDevice->pid == desc.idProduct)
        {
            struct libusb_device_handle *usb_p = NULL;
            char szSerialNumber[64] = {0x0};
            libusb_open(devs[i], &usb_p);
            if(NULL != usb_p)
            {
                //libusb_get_string_descriptor_ascii(usb_p,desc.iSerialNumber,(unsigned char*)szSerialNumber, 64);
                unsigned int tickstart = GetTickCount();
                while(GetTickCount() - tickstart < 3 * 1000)
                {
                    Sleep(10);
                    int ret = libusb_get_string_descriptor_ascii(usb_p, desc.iSerialNumber, (unsigned char *)szSerialNumber, 64);
                    if(ret >= 0)
                    {
                        break;
                    }
                    if(desc.idVendor != 0x1b55)
                    {
                        break;
                    }
                }
                libusb_close(usb_p);
            }
            if((strlen(usbDevice->szSerialNumber) > 0 && strcmp(usbDevice->szSerialNumber, "0") != 0 && strcmp(usbDevice->szSerialNumber, szSerialNumber) == 0) ||
                    ((strlen(usbDevice->szSerialNumber) <= 0 || strcmp(usbDevice->szSerialNumber, "0") == 0) && (libusb_get_bus_number(devs[i]) == usbDevice->bus_number && libusb_get_device_address(devs[i]) == usbDevice->device_address))
              )
            {
                dev = devs[i];
                break;
            }
        }
    }
    if(NULL != dev)
    {
        void *handle = xlibusbopen(ctx, dev, usbDevice->vid, usbDevice->pid, usbDevice->szSerialNumber);
        if(NULL != handle)
        {
            libusb_free_device_list(devs, 1); //free the list, unref the devices in it
            return handle;
        }
    }
    libusb_free_device_list(devs, 1); //free the list, unref the devices in it
    libusb_exit(ctx);
    return NULL;
#endif
    return NULL;
}

int APICALL x_libusb_closedevice(void *handle)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
#ifdef __ANDROID__
    if(1 == pXUSBDeviceHandle->bUsbHost)
    {
        LEAVELOCK(pXUSBDeviceHandle->lock);
        return -1;
    }
#endif
    pXUSBDeviceHandle->magic = 0x0;
#ifndef _WIN32
    if(NULL != pXUSBDeviceHandle->dev_handle)
    {
        libusb_reset_device(pXUSBDeviceHandle->dev_handle);
        libusb_release_interface(pXUSBDeviceHandle->dev_handle, pXUSBDeviceHandle->interfaceIndex);
        libusb_close(pXUSBDeviceHandle->dev_handle);
    }
    if(NULL != pXUSBDeviceHandle->ctx)
    {
        libusb_exit(pXUSBDeviceHandle->ctx);
    }
#endif
    LEAVELOCK(pXUSBDeviceHandle->lock);
    RLEASELOCK(pXUSBDeviceHandle->lock);
    delete pXUSBDeviceHandle;

    return 0;
}

#ifdef __ANDROID__
void *APICALL x_libusb_opendevice_fd(int vid, int pid, int busNum, int dev_address, int fd)
{
    libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
    libusb_device *dev = NULL;
    libusb_context *ctx = NULL; //a libusb session
    int r; //for return values
    int retVal = 0;
    ssize_t cnt; //holding number of devices in list
    r = libusb_init(&ctx); //initialize the library for the session we just declared
    if(r < 0)
    {
        return NULL;
    }
    //libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_INFO); //set verbosity level to 3, as suggested in the documentation

    cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
    if(cnt < 0)
    {
        libusb_exit(ctx);
        return NULL;
    }

    for(int i = 0; i < cnt; i++)
    {
        if(fd == 0 && libusb_get_bus_number(devs[i]) == busNum && libusb_get_device_address(devs[i]) == dev_address)
        {
            dev = devs[i];
            break;
        }
        else
        {
            struct libusb_device_descriptor desc;
            int r2 = libusb_get_device_descriptor(devs[i], &desc);
            if(r2 < 0)
            {
                continue;
            }
            if(vid == desc.idVendor && pid == desc.idProduct)
            {
                dev = devs[i];
                break;
            }
        }
    }
    libusb_free_device_list(devs, 1); //free the list, unref the devices in it
    if(NULL != dev)
    {
        void *handle = xlibusbopen(ctx, dev, vid, pid, NULL, fd);
        if(NULL != handle)
        {
            return handle;
        }
    }
    libusb_exit(ctx);
    return NULL;
}

void *APICALL x_libusb_opendevice_usbhost(JavaVM *jvm, JNIEnv *env, jobject obj)
{
    if(NULL == g_jvm && NULL != jvm)
    {
        g_jvm = jvm;
    }
    return xusbhostopen(jvm, env, obj);
}

int APICALL x_libusb_closedevice_usbhost(JavaVM *jvm, JNIEnv *env, void *handle)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    if(1 != pXUSBDeviceHandle->bUsbHost)
    {
        LEAVELOCK(pXUSBDeviceHandle->lock);
        return -1;
    }
    pXUSBDeviceHandle->magic = 0x0;
    pXUSBDeviceHandle->bUsbHost = 0;
    if(NULL != pXUSBDeviceHandle->obj)
    {
        env->DeleteGlobalRef(pXUSBDeviceHandle->obj);
    }
    if(NULL != pXUSBDeviceHandle->tempBufferArr)
    {
        env->DeleteGlobalRef(pXUSBDeviceHandle->tempBufferArr);
    }
    if(NULL != pXUSBDeviceHandle->pRecvBuffer)
    {
        delete[] pXUSBDeviceHandle->pRecvBuffer;
    }
    LEAVELOCK(pXUSBDeviceHandle->lock);
    RLEASELOCK(pXUSBDeviceHandle->lock);
    delete pXUSBDeviceHandle;
    return 0;
}
#endif

int APICALL x_libusb_controltransfer(void *handle, unsigned char bmRequestType, unsigned char bRequest, unsigned short wValue, unsigned short wIndex,
                                     unsigned char *data, unsigned short wLength, unsigned int timeout)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    Sleep(1);
#ifdef ZKDEBUG
    printf("XUSBDevAPI_ControlTransfer requestType=%02X,request=%02X, value=%d, index=%d\r\n", bmRequestType, bRequest, wValue, wIndex);
#endif
    ret = IMPL_ControlTransfer(handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
#ifdef ZKDEBUG
    if(ret > 0)
    {
        printf("ctl>>\r\n");
        printHex(data, ret);
    }
#endif
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}

int APICALL x_libusb_transferwrite(void *handle, unsigned char *data, int length, int *transferred, unsigned int timeout)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    Sleep(1);
    ret = IMPL_TransferWrite(handle, data, length, transferred, timeout);
#ifdef ZKDEBUG
    printf("XUSBDevAPI_TransferWrite send(outEp=%d, length:%d, ret:%d,write length:%d)>>\r\n", pXUSBDeviceHandle->outEndpoint, length, ret, transferred);
    printHex(data, length);
#endif
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}

int APICALL x_libusb_transferread(void *handle, unsigned char *data, int length, int *transferred, unsigned int timeout)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ret = IMPL_TransferRead(handle, data, length, transferred, timeout);
#ifdef ZKDEBUG
    printf("XUSBDevAPI_TransferRead, inEP=%d,  read length=%d, ret=%d, transferred=%d\r\n", pXUSBDeviceHandle->inEndpoint, length, ret, *transferred);
    if(0 == ret && *transferred > 0)
    {
        printf("receive>>\r\n");
        printHex(data, *transferred);
    }
#endif
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}


void APICALL x_libusb_resetdevice(void *handle)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return;
    }
#ifdef __ANDROID__
    if(0 == pXUSBDeviceHandle->bUsbHost)
#endif
    {
#ifndef _WIN32
        libusb_release_interface(pXUSBDeviceHandle->dev_handle, pXUSBDeviceHandle->interfaceIndex);
        libusb_reset_device(pXUSBDeviceHandle->dev_handle);
        libusb_claim_interface(pXUSBDeviceHandle->dev_handle, pXUSBDeviceHandle->interfaceIndex);
#endif

    }
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return;
}

int APICALL  x_libusb_getserial(void *handle, char *serial, int size)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    memset(serial, 0x0, size);
    if(strlen(pXUSBDeviceHandle->szSerialNumber) == 0)
    {
        strcpy(serial, pXUSBDeviceHandle->szSerialNumber);
        ret = strlen(pXUSBDeviceHandle->szSerialNumber);
    }
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}
int APICALL  x_libusb_getmodel(void *handle,  unsigned char *buffer, int size)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    memset(buffer, 0x0, size);
    if(strlen(pXUSBDeviceHandle->szModel) == 0)
    {
        strcpy((char *)buffer, pXUSBDeviceHandle->szModel);
        ret = strlen(pXUSBDeviceHandle->szModel);
    }
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}

int APICALL  x_libusb_getmanufacturer(void *handle,  unsigned char *buffer, int size)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    memset(buffer, 0x0, size);
    if(strlen(pXUSBDeviceHandle->szManufacturer) == 0)
    {
        strcpy((char *)buffer, pXUSBDeviceHandle->szManufacturer);
        ret = strlen(pXUSBDeviceHandle->szManufacturer);
    }
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}

int APICALL x_libusb_get_vid_pid_rev(void *handle, int *nVID, int *nPID, int *nREV)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return -1;
    }
    *nVID = pXUSBDeviceHandle->vid;
    *nPID = pXUSBDeviceHandle->pid;
    *nREV = pXUSBDeviceHandle->rev;
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return 0;
}

int APICALL x_libusb_get_siosetdata(void *handle)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    int ret = 0;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return 0;
    }
    ret = pXUSBDeviceHandle->currentSioSetData;
    LEAVELOCK(pXUSBDeviceHandle->lock);
    return ret;
}

void APICALL x_libusb_set_siosetdata(void *handle, int data)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return;
    }
    pXUSBDeviceHandle->currentSioSetData = data;
    LEAVELOCK(pXUSBDeviceHandle->lock);
}

void APICALL x_libusb_get_bcddevice_iserialNumber(void *handle, int *bcdDevice, int *iSerialNumber)
{
    PXUSBDeviceHandle pXUSBDeviceHandle = (PXUSBDeviceHandle)handle;
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return;
    }
    ENTERLOCK(pXUSBDeviceHandle->lock);
    if(MAGIC_DEVICE_HANDLE != pXUSBDeviceHandle->magic)
    {
        return;
    }
    *bcdDevice = pXUSBDeviceHandle->bcdDevice;
    *iSerialNumber = pXUSBDeviceHandle->iSerialNumber;
    LEAVELOCK(pXUSBDeviceHandle->lock);
}

XUDC_API_END

